<template>
  <div
    class="vue-draggable-window"
    :style="style"
    @mousedown="startDrag"
    @touchstart="startDrag"
    ref="draggableElement"
  >
    <slot></slot>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from "vue";

const props = defineProps({
  // 初始位置
  initialPosition: {
    type: Object,
    default: () => ({ x: 0, y: 0 }),
  },
  // 是否启用拖拽
  disabled: {
    type: Boolean,
    default: false,
  },
  // 边界偏移量（防止元素完全移出屏幕）
  boundaryOffset: {
    type: Number,
    default: 10,
  },
});

const emit = defineEmits(["dragging", "drag-end"]);

const draggableElement = ref<HTMLElement | null>(null);
const position = ref<any>({ ...props.initialPosition });
const isDragging = ref(false);
const startPos = ref<{ x: number; y: number }>({ x: 0, y: 0 });
const startMouse = ref<{ x: number; y: number }>({ x: 0, y: 0 });
const windowSize = ref<{ width: number; height: number }>({
  width: window.innerWidth,
  height: window.innerHeight,
});

const style: any = computed(() => ({
  position: "fixed",
  left: `${position.value.x}px`,
  top: `${position.value.y}px`,
  cursor: props.disabled ? "default" : isDragging.value ? "grabbing" : "grab",
  userSelect: "none",
  touchAction: "none",
  zIndex: isDragging.value ? 1000 : "auto",
}));

const updateWindowSize = () => {
  windowSize.value = {
    width: window.innerWidth,
    height: window.innerHeight,
  };
  // 确保元素不会超出新窗口边界
  constrainPosition();
};

const constrainPosition = () => {
  if (!draggableElement.value) return;

  const elRect = draggableElement.value.getBoundingClientRect();
  const maxX = windowSize.value.width - elRect.width - props.boundaryOffset;
  const maxY = windowSize.value.height - elRect.height - props.boundaryOffset;

  position.value = {
    x: Math.max(props.boundaryOffset, Math.min(position.value.x, maxX)),
    y: Math.max(props.boundaryOffset, Math.min(position.value.y, maxY)),
  };
};

const startDrag = (e: any) => {
  if (props.disabled) return;

  e.preventDefault();
  isDragging.value = true;

  // 获取初始鼠标位置
  const clientX = e.clientX || e.touches[0].clientX;
  const clientY = e.clientY || e.touches[0].clientY;
  startMouse.value = { x: clientX, y: clientY };

  // 获取元素当前的位置
  startPos.value = { ...position.value };

  // 添加事件监听
  document.addEventListener("mousemove", onDrag);
  document.addEventListener("touchmove", onDrag, { passive: false });
  document.addEventListener("mouseup", stopDrag);
  document.addEventListener("touchend", stopDrag);
};

const onDrag = (e: any) => {
  if (!isDragging.value) return;
  e.preventDefault();

  const clientX = e.clientX || e.touches[0].clientX;
  const clientY = e.clientY || e.touches[0].clientY;

  // 计算新位置
  let newX = startPos.value.x + (clientX - startMouse.value.x);
  let newY = startPos.value.y + (clientY - startMouse.value.y);

  // 确保不会超出窗口边界
  if (draggableElement.value) {
    const elRect = draggableElement.value.getBoundingClientRect();
    const maxX = windowSize.value.width - elRect.width - props.boundaryOffset;
    const maxY = windowSize.value.height - elRect.height - props.boundaryOffset;

    newX = Math.max(props.boundaryOffset, Math.min(newX, maxX));
    newY = Math.max(props.boundaryOffset, Math.min(newY, maxY));
  }

  position.value = { x: newX, y: newY };
  emit("dragging", position.value);
};

const stopDrag = () => {
  if (!isDragging.value) return;

  isDragging.value = false;
  emit("drag-end", position.value);

  // 移除事件监听
  document.removeEventListener("mousemove", onDrag);
  document.removeEventListener("touchmove", onDrag);
  document.removeEventListener("mouseup", stopDrag);
  document.removeEventListener("touchend", stopDrag);
};

// 外部可以调用的方法，用于设置位置
const setPosition = (x: any, y: any) => {
  position.value = { x, y };
  constrainPosition();
};

// 暴露方法给父组件
defineExpose({
  setPosition,
});

onMounted(() => {
  updateWindowSize();
  window.addEventListener("resize", updateWindowSize);
  // 初始位置约束
  constrainPosition();
});

onBeforeUnmount(() => {
  stopDrag();
  window.removeEventListener("resize", updateWindowSize);
});
</script>

<style scoped>
.vue-draggable-window {
  transition: transform 0.1s ease;
}

.vue-draggable-window:active {
  transition: none;
}
</style>
